module nade.texture;

import std.file;
import std.string;

import derelict.sdl.sdl;
import derelict.opengl.gl;


class Texture {
	static GLuint load(string path)
	{
		if(!exists(path))
			return 0;

		auto src = SDL_LoadBMP(cast(char*)toStringz(path));

		static uint getPixel(SDL_Surface* surface, int x, int y)
		{
			int bpp = surface.format.BytesPerPixel;
			ubyte *p = cast(ubyte*)surface.pixels + y*surface.pitch + x*bpp;

			switch(bpp)
			{
				case 1: return *p;
				case 2: return *cast(ushort*)p;
				case 3:
				{
					if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
						return (p[0] << 16) | (p[1] << 8) | p[2];
					else
						return p[0] | (p[1] << 8) | (p[2] << 16);
				}
				case 4: return *cast(uint*)p;
				default:
			}

			return 0;
		}

		int next_p2(int a)
		{
			int result = 1;
			while(result < a) result <<= 1;
			return result;
		}
		int pw = next_p2(src.w);
		int ph = next_p2(src.h);
		_tx = src.w/cast(real)pw;
		_ty = src.h/cast(real)ph;

		ubyte[] data;
		data.length = pw*ph*4;

		const magenta = SDL_MapRGB(src.format, 0xff, 0x00, 0xff);
		for(int x = 0;  x < src.w;  ++x)
		{
			for(int y = 0;  y < src.h; ++y)
			{
				const dp = (x + y*pw)*4;

				const p = getPixel(src, x, src.h - y - 1);
				if(p == magenta)
				{
					data[dp] = 0x00;
					data[dp + 1] = 0x00;
					data[dp + 2] = 0x00;
					data[dp + 3] = 0x00;
				}
				else
				{
					SDL_GetRGB(p, src.format, &data[dp], &data[dp + 1], &data[dp + 2]);
					data[dp + 3] = 0xff;
				}
			}
		}

		GLuint texture = 0;
		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pw, ph, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.ptr);

		return texture;
	}
	static GLuint load(string path, string maskPath)
	{
		if(!exists(path))
			return 0;

		auto src = SDL_LoadBMP(cast(char*)toStringz(path));
		auto msrc = SDL_LoadBMP(cast(char*)toStringz(maskPath));
		assert(src.w == msrc.w);

		static uint getPixel(SDL_Surface* surface, int x, int y)
		{
			int bpp = surface.format.BytesPerPixel;
			ubyte *p = cast(ubyte*)surface.pixels + y*surface.pitch + x*bpp;

			switch(bpp)
			{
				case 1: return *p;
				case 2: return *cast(ushort*)p;
				case 3:
				{
					if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
						return (p[0] << 16) | (p[1] << 8) | p[2];
					else
						return p[0] | (p[1] << 8) | (p[2] << 16);
				}
				case 4: return *cast(uint*)p;
				default:
			}

			return 0;
		}

		int next_p2(int a)
		{
			int result = 1;
			while(result < a) result <<= 1;
			return result;
		}
		int pw = next_p2(src.w);
		int ph = next_p2(src.h);
		_tx = src.w/cast(real)pw;
		_ty = src.h/cast(real)ph;

		ubyte[] data;
		data.length = pw*ph*4;

		const magenta = SDL_MapRGB(src.format, 0xff, 0x00, 0xff);
		for(int x = 0;  x < src.w;  ++x)
		{
			for(int y = 0;  y < src.h; ++y)
			{
				const dp = (x + y*pw)*4;

				const p = getPixel(src, x, src.h - y - 1);
				const mp = getPixel(msrc, x, (src.h - y - 1)%msrc.h);
				ubyte t;
				SDL_GetRGB(p, src.format, &data[dp], &data[dp + 1], &data[dp + 2]);
				SDL_GetRGB(mp, msrc.format, &data[dp + 3], &t, &t);
			}
		}

		GLuint texture = 0;
		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pw, ph, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.ptr);

		return texture;
	}
	static GLuint load(string path, string maskPath, int mx, int my)
	{
		if(!exists(path))
			return 0;

		auto src = SDL_LoadBMP(cast(char*)toStringz(path));
		auto msrc = SDL_LoadBMP(cast(char*)toStringz(maskPath));

		static uint getPixel(SDL_Surface* surface, int x, int y)
		{
			int bpp = surface.format.BytesPerPixel;
			ubyte *p = cast(ubyte*)surface.pixels + y*surface.pitch + x*bpp;

			switch(bpp)
			{
				case 1: return *p;
				case 2: return *cast(ushort*)p;
				case 3:
				{
					if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
						return (p[0] << 16) | (p[1] << 8) | p[2];
					else
						return p[0] | (p[1] << 8) | (p[2] << 16);
				}
				case 4: return *cast(uint*)p;
				default:
			}

			return 0;
		}

		int next_p2(int a)
		{
			int result = 1;
			while(result < a) result <<= 1;
			return result;
		}
		int pw = next_p2(src.w);
		int ph = next_p2(src.h);
		_tx = src.w/cast(real)pw;
		_ty = src.h/cast(real)ph;

		ubyte[] data;
		data.length = pw*ph*4;

		const magenta = SDL_MapRGB(src.format, 0xff, 0x00, 0xff);
		for(int x = 0;  x < src.w;  ++x)
		{
			for(int y = 0;  y < src.h; ++y)
			{
				const dp = (x + y*pw)*4;

				const p = getPixel(src, x, src.h - y - 1);
				SDL_GetRGB(p, src.format, &data[dp], &data[dp + 1], &data[dp + 2]);

				if(x >= mx  &&  x < (mx + msrc.w)  &&  (src.h - y - 1) >= my  &&  (src.h - y - 1) < (my + msrc.h))
				{
					const mp = getPixel(msrc, x - mx, (src.h - y - 1) - my);
					ubyte t;
					SDL_GetRGB(mp, msrc.format, &data[dp + 3], &t, &t);
				}
				else
				{
					data[dp + 3] = 0;
				}
			}
		}

		GLuint texture = 0;
		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pw, ph, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.ptr);

		return texture;
	}
	static GLuint loadAlphaMap(string path)
	{
		if(!exists(path))
			return 0;

		auto src = SDL_LoadBMP(cast(char*)toStringz(path));

		static uint getPixel(SDL_Surface* surface, int x, int y)
		{
			int bpp = surface.format.BytesPerPixel;
			ubyte *p = cast(ubyte*)surface.pixels + y*surface.pitch + x*bpp;

			switch(bpp)
			{
				case 1: return *p;
				case 2: return *cast(ushort*)p;
				case 3:
				{
					if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
						return (p[0] << 16) | (p[1] << 8) | p[2];
					else
						return p[0] | (p[1] << 8) | (p[2] << 16);
				}
				case 4: return *cast(uint*)p;
				default:
			}

			return 0;
		}

		int next_p2(int a)
		{
			int result = 1;
			while(result < a) result <<= 1;
			return result;
		}
		int pw = next_p2(src.w);
		int ph = next_p2(src.h);
		_tx = src.w/cast(real)pw;
		_ty = src.h/cast(real)ph;

		ubyte[] data;
		data.length = pw*ph;

		for(int x = 0;  x < src.w;  ++x)
		{
			for(int y = 0;  y < src.h; ++y)
			{
				const dp = x + y*pw;

				const p = getPixel(src, x, src.h - y - 1);
				ubyte dm1, dm2;
				SDL_GetRGB(p, src.format, &data[dp], &dm1, &dm2);
			}
		}

		GLuint texture = 0;
		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, pw, ph, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data.ptr);

		return texture;
	}

	static {
		real		_tx, _ty;
	}
}
